package com.jbm.core.common;

import com.jbm.core.common.ResponseRecvCallback.ResponseRecvCallback;
import com.jbm.core.iface.IAcross;
import com.jbm.core.pb.PBIAcross;
import com.jbm.core.server.AccountCallback;
import com.jbm.core.server.SAccount;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.util.HashMap;

/**
 * Created by root on 17-2-27.
 */
public class AutoCall
{
	public final static AutoCall g_autocall = new AutoCall();

	protected String[] m_iface_classes;
	protected String[] m_call_classes;

	protected Resource m_dialogqueue = null;
	protected HashMap<String,Integer> m_forcecall;

	protected final Object m_sa_mutex = new Object();
	protected final Object m_ca_mutex = new Object();

	public AutoCall()
	{
	}

	public String[] getM_iface_classes()
	{
		return m_iface_classes;
	}

	public void InitAC (int rescnt, String forcecall, String iface_classes, String call_classes )
	{
		m_dialogqueue = new Resource(rescnt,"dialog");

		if ( forcecall != null )
		{
			m_forcecall = new HashMap<String, Integer>();

			String[] mf = forcecall.split("\\|");
			for (int i = 0; i < mf.length; i++)
				m_forcecall.put(mf[i], 1);
		}

		if ( iface_classes != null )
			m_iface_classes = iface_classes.split("\\|");
		if ( call_classes != null )
			m_call_classes = call_classes.split("\\|");
	}

	public P2PDialog GetDialog (PBIAcross.IAcross iacross)
	{
		return (P2PDialog)m_dialogqueue.Get(new ResKey(iacross.getQuery()),iacross);
	}

	public IAcross Call (IoSession iosession,
						 String classname, String method,
						 IAcross content, int fromid, int toid, String fromtag, String totag, int index,
						 boolean hasresp, boolean waitresp, ResponseRecvCallback rrcallback )
	{
		IAcross response = null;

		P2PDialog p2pdialog = new P2PDialog(10);
		p2pdialog.Born(6, 500);
		p2pdialog.Bind_IoSession(iosession);
		p2pdialog.Bind_RRCallback(rrcallback);

		m_dialogqueue.Add(p2pdialog);

		PBIAcross.IAcross iacross = new PBRandR().BuildRequest(
				p2pdialog.GetLifeName().toString(),
				classname,
				method,
				fromid,
				toid,
				fromtag,
				totag,
				index,
				content );

		if ( !iacross.hasCode() )
		{
			p2pdialog.Bind_Request(new PBRandR(iacross));
			p2pdialog.setM_content((IAcross)content);

			iosession.write(iacross);

/*
			IoBuffer iobuf = IoBuffer.allocate(100).setAutoExpand(true);
			iobuf.put(iacross.toByteArray());
			iobuf.flip();

			iosession.write(iobuf);
*/

			if (!hasresp)
				p2pdialog.ReleaseFromPool();
			else if (waitresp)
			{
				PBIAcross.IAcross respiacross = (PBIAcross.IAcross) p2pdialog.Get();

				response = new TransClass().PB2_0toI2_0("Client:", respiacross);
			}
		}else
		{
			response = new IAcross(iacross.getCode(),iacross.getInfo());
			p2pdialog.ReleaseFromPool();
		}

		return response;
	}

	public boolean Relay (IoSession iosession, PBIAcross.IAcross pbrequest, int local_id)
	{
		int tarid = 0;

		if ( ( pbrequest.getRandr() & RandR.RANDR_TYPE_RESPONSE ) == RandR.RANDR_TYPE_RESPONSE )
		{
			if (pbrequest.hasFromid())
				tarid = pbrequest.getFromid();
		}else
		{
			if (pbrequest.hasToid())
				tarid = pbrequest.getToid();
		}

		if ( tarid != 0 && tarid != local_id && local_id != 0 )
		{
			ConnectAgent ca = null;

			if ( MainServer.g_server.getM_server_type() == MainServer.Server_type_Server )
			{
				SAccount sa = AccountCtrl.g_accountctrl.GetSA(tarid);

				if ( sa != null )
					ca = sa.GetCA(pbrequest.getTotag());
			}else if ( MainServer.g_server.getM_server_type() == MainServer.Server_type_Client )
			{
				ca = AccountCtrl.g_accountctrl.GetCA(tarid,pbrequest.getTotag());
			}

			if ( ca != null )
			{
/*
				P2PDialog dialog = new P2PDialog(10);
				dialog.Born(6, 500);
				dialog.Bind_PrevIoS(iosession, pbrequest.getQuery());

				m_dialogqueue.Add(dialog);

				PBIAcross.IAcross.Builder builder = pbrequest.toBuilder();
				builder.setQuery(dialog.GetLifeName().toString());
				pbrequest = builder.build();

				dialog.Bind_Request(new PBRandR(pbrequest));
*/

				if (!ca.Send(pbrequest,ConnectAgent.Send_Type_Any))
					ca = null;
			}

			if ( ca == null )
			{
				PBIAcross.IAcross pbresponse = new PBRandR(pbrequest).BuildResponse(new IAcross(200,"BUFFER"));

				IoBuffer iobuf = IoBuffer.allocate(100).setAutoExpand(true);
				iobuf.put(pbresponse.toByteArray());
				iobuf.flip();
				iosession.write(iobuf);
			}

			return true;
		}

		return false;
	}

	public void UpdatePoint (IoSession iosession, Integer login_id, String app_type )
	{
		String remotehost = ((InetSocketAddress) iosession.getRemoteAddress()).getAddress().getHostAddress();
		int remoteport = ((InetSocketAddress) iosession.getRemoteAddress()).getPort();

		ConnectAgent ca = AccountCtrl.g_accountctrl.GetorAddCA(remotehost+":"+remoteport,null,false);

		UpdatePoint(ca,login_id,app_type);

	}

	public void UpdatePoint (ConnectAgent ca, Integer login_id, String app_type )
	{
		if ( login_id == null || login_id == 0 || ca == null )
			return;

		SAccount sacc = null;

		synchronized (m_sa_mutex)
		{
			sacc = AccountCtrl.g_accountctrl.GetSA(login_id);

			if (sacc == null)
			{
				sacc = new SAccount(login_id);
				sacc.Born(0, 3600000);

				AccountCtrl.g_accountctrl.AddSA(sacc);
				sacc.Start(app_type);
			}
		}

		sacc.Push2Map(ca,new AccountCallback(sacc));
	}

	public void Invoke (IoSession iosession, PBIAcross.IAcross pbrequest, int local_id)
	{
		String classname = null;

		Class<?> cls = null;
		IAcross sr = null;
		IAcross response = null;

		ConnectAgent cur_ca = null;

		if ( MainServer.g_server.getM_server_type() == MainServer.Server_type_Client)
		{
			System.out.println("Client Invoke..........\n"+pbrequest.toString());

			cur_ca = AccountCtrl.g_accountctrl.GetCA(pbrequest.getFromid(),pbrequest.getFromtag());

			if ( cur_ca == null )
				return;

			if (!cur_ca.Match_QIndex(pbrequest.getQindex()))
				return;
		}else
		{
			System.out.println("Server Invoke..........\n"+pbrequest.toString());

			ConnectAgent ca = new ConnectAgent(pbrequest.getFromtag(),
					((InetSocketAddress) iosession.getRemoteAddress()).getAddress().getHostAddress(),
					((InetSocketAddress) iosession.getRemoteAddress()).getPort(),
					((InetSocketAddress) iosession.getLocalAddress()).getPort(),
					MainServer.Server_type_Server);

			ca.Born(0,30000);
			ca.setM_keepalive(false);
			ca.setM_iosession(iosession);

			String ca_key = ((InetSocketAddress) iosession.getRemoteAddress()).getAddress().getHostAddress()+
					":"+((InetSocketAddress) iosession.getRemoteAddress()).getPort();

			cur_ca = AccountCtrl.g_accountctrl.GetorAddCA(ca_key,ca,pbrequest.getClass_().equals("1st"));

			System.out.println("ca_key="+ca_key);

			if (cur_ca == null)
			{
				System.out.println("CA is null");
				return;
			}else
				cur_ca.setM_iosession(iosession);

			if (!cur_ca.Match_QIndex(pbrequest.getQindex()))
			{
				System.out.println("QIndex Match Wrong");
				return;
			}

			if (cur_ca.GetStatus() != Life.life_status_authorized)
				if (m_forcecall.get(pbrequest.getClass_() + "." + pbrequest.getMethod()) == null)
					response = new IAcross(401, "unauthorized");

			iosession.setAttribute("CA_Key", cur_ca);

			if ( response == null &&
					( pbrequest.getClass_().equals("keepalive")||
							pbrequest.getClass_().equals("1st")))
				response = new IAcross(200, "OK");
		}

		if ( response == null )
		{
			IAcross request = new TransClass().PB2_0toI2_0("Server:", pbrequest);

			request.m_ca = cur_ca;

			if (request.m_code == 0)
			{
				for (int i = 0; i < m_call_classes.length; i++)
				{
					classname = m_call_classes[i] + ".remote.R" + pbrequest.getClass_();

					try
					{
						cls = Class.forName(classname);
					} catch (Exception e)
					{
						sr = new IAcross(404, pbrequest.getClass_() + " not found");
					}

					if (cls != null)
						break;
					else
					{
						classname = m_call_classes[i] + "." + pbrequest.getClass_();

						try
						{
							cls = Class.forName(classname);
						} catch (Exception e)
						{
							sr = new IAcross(404, pbrequest.getClass_() + " not found");
						}

						if (cls != null)
							break;
					}
				}

				if (cls != null)
				{
					Object obj = null;
					Constructor c = null;

					try
					{
						c = cls.getConstructor(IAcross.class);
					} catch (Exception e)
					{
						sr = new IAcross(500, pbrequest.getClass_() + "(IAcross) getConstructor error");
					}

					if (c != null)
					{
						try
						{
							obj = c.newInstance(request);
						} catch (Exception e)
						{
							sr = new IAcross(500, pbrequest.getClass_() + "(IAcross) newInstance failed");
						}

						if (obj != null)
						{
							Method method = null;

							try
							{
								method = obj.getClass().getMethod(pbrequest.getMethod());
							} catch (Exception e)
							{
								sr = new IAcross(405, pbrequest.getClass_() + "." + pbrequest.getMethod() + "() not found");
							}

							if (method != null)
							{
								try
								{
									response = (IAcross) method.invoke(obj);
								} catch (Exception e)
								{
									sr = new IAcross(500, pbrequest.getClass_() + "." + pbrequest.getMethod() + "() invoke error");
								}
							}
						}
					}
				}

				if (response == null)
					response = sr;
			} else
				response = new IAcross(request.m_code, request.m_info);
		}

		if ( response != null )
		{
			if ( cur_ca != null )
			{
				if ( cur_ca.GetLifeName() != null )
					response.m_ca_key = cur_ca.GetLifeName().toString();

				if (response.m_code <= 200)
					cur_ca.IamLiving();
			}

			PBIAcross.IAcross pbresponse = new PBRandR(pbrequest).BuildResponse((IAcross) response);

			iosession.write(pbresponse);

/*
			IoBuffer iobuf = IoBuffer.allocate(100).setAutoExpand(true);
			iobuf.put(pbresponse.toByteArray());
			iobuf.flip();
			iosession.write(iobuf);
*/
		}
	}
}
